#include <limits.h>
#include "PythonGrammar.h"
#include "SimpleNode.h"
#include "SpecialStr.h"
#include "JJTState.h"
#include "ast/Call.h"
#include "ast/commentType.h"
#include "ast/exprType.h"
#include "ast/JfpDef.h"
#include "TokensIterator.h"

class ObjectAndStrategy: public Object
{
public:
    ObjectPtr obj;
    int strategy;

    ObjectAndStrategy(const ObjectPtr& objp, int strategyp):
        obj(objp),
        strategy(strategyp)
    {
    }
};

typedef QSharedPointer<ObjectAndStrategy> ObjectAndStrategyPtr;

PythonGrammar::PythonGrammar():
    GrammarErrorHandlers()
{
    _tokensIterator = NULL;
    STRATEGY_BEFORE_NEXT = 1;
    STRATEGY_ADD_AFTER_PREV = 0;
    DEFAULT_SEARCH_ON_LAST = false;
}

PythonGrammar::~PythonGrammar()
{
    delete _tokensIterator;
}

SimpleNodePtr PythonGrammar::findTokenToAdd(const TokenPtr& next)
{
    SimpleNodePtr curr = getJJTree()->peekNode();
    if (curr != prev) {
        //let's see which one is better suited
        if (prev->beginLine == next->beginLine) {
            return prev;
        }
        if (curr->beginLine == next->beginLine) {
            return curr;
        }
        //if it was found later than both, let's get the current
        if (next->beginLine > prev->beginLine && next->beginLine > curr->beginLine) {
            return curr;
        }

    }
    return prev;
}

bool PythonGrammar::findTokenAndAdd(const QString & token, bool searchOnLast)
{
    SpecialStrPtr s = createSpecialStr(token, searchOnLast);
    getTokenSourceSpecialTokensList()->append(ObjectPtr(new ObjectAndStrategy(s, STRATEGY_ADD_AFTER_PREV)));
    return true;
}

void PythonGrammar::addToPeek(const QString& t, bool after)
{
    //qDebug() << "add to peek" << t;
}

void PythonGrammar::addToPeek(const ObjectPtr& t, bool after, Ast::AstTypes class_)
{
    SimpleNodePtr peeked = getJJTree()->peekNode();
    if (peeked && t)
        addToPeek(peeked, t, after, class_);
}

void PythonGrammar::addToPeek(const SimpleNodePtr& peeked, const ObjectPtr& t, bool after, Ast::AstTypes class_)
{
    if (class_ != Ast::Object) {
        // just check if it is the class we were expecting.
        if (peeked->type() != class_) {
            throw QString("Error, expecting class: %1 received class: %2 Representation: %3").arg(class_).arg(peeked->type()).arg(peeked->toString());
        }
    }
    SpecialStrPtr st = convertStringToSpecialStr(t);
    if(st){
        addSpecial(peeked, st, after);
    }
}

void PythonGrammar::addToPeekCallFunc(const ObjectPtr& t, bool after)
{
    CallPtr n = getJJTree()->peekNode<Call>();
    if (n)
        addSpecial(exprTypePtr(n->func()), t, after);
}

void PythonGrammar::addSpecial(const SimpleNodePtr& node, const ObjectPtr& special, bool after)
{
    TokenPtr t = special.dynamicCast<Token>();
    ObjectPtr newSpecial = special;
    if(t != NULL){
        if(t->toString().trimmed().startsWith("#")){
            commentTypePtr comment(new commentType(t->image.trimmed()));
            comment->beginColumn = t->beginColumn;
            comment->beginLine = t->beginLine;
            newSpecial = comment;

            if(node->beginLine != comment->beginLine){
                if(_lastSpecial != NULL && _lastNodeWithSpecial != NULL){
                    if(comment->beginLine < _lastSpecial->beginLine || (comment->beginLine == _lastSpecial->beginLine && comment->beginColumn < _lastSpecial->beginCol)){
                        QList<ObjectPtr> specialsBefore = _lastNodeWithSpecial->getSpecialsBefore();
                        specialsBefore.insert(specialsBefore.indexOf(_lastSpecial), comment);
                        return;
                    }
                }
            }
        } else {
            newSpecial = t->asSpecialStr().dynamicCast<SimpleNode>();
        }
    }

    node->addSpecial(newSpecial, after);
}

void PythonGrammar::addSpecialTokenToLastOpened(const QString & o)
{
    addSpecialTokenToLastOpened(createSpecialStr(o.trimmed(), DEFAULT_SEARCH_ON_LAST, false));
}

void PythonGrammar::addSpecialTokenToLastOpened(const ObjectPtr& o)
{
    SpecialStrPtr no = convertStringToSpecialStr(o);
    if(no){
        SimpleNodePtr lastOpened = getJJTree()->getLastOpened();
        _lastSpecial =no;
        _lastNodeWithSpecial = lastOpened;

        lastOpened->specialsBefore.append(no);
    }
}

void PythonGrammar::addSpecialToken(const ObjectPtr& o, int strategy)
{
    SpecialStrPtr t = convertStringToSpecialStr(o);
    if(t){
        getTokenSourceSpecialTokensList()->append(ObjectPtr(new ObjectAndStrategy(t, strategy)));
    }
}

void PythonGrammar::addSpecialToken(const QString & o, int strategy)
{
    SpecialStrPtr t = createSpecialStr(o.trimmed(), DEFAULT_SEARCH_ON_LAST, false);
    if(t){
        getTokenSourceSpecialTokensList()->append(ObjectPtr(new ObjectAndStrategy(t, strategy)));
    }
}

void PythonGrammar::addSpecialToArgDef(const ObjectPtr& str)
{
    TokenPtr token = str.dynamicCast<Token>();
    SimpleNodePtr peeked = getJJTree()->peekNode();
    if (peeked.dynamicCast<JfpDef>()) {
        JfpDefPtr jfpdef = peeked.dynamicCast<JfpDef>();
        if(jfpdef->typeDef){
            jfpdef->typeDef->specialsAfter.append(token->asSpecialStr());
        }else{
            jfpdef->nameNode->specialsAfter.append(token->asSpecialStr());
        }
    }else{
        peeked->specialsAfter.append(token->asSpecialStr());
    }
}


void PythonGrammar::jjtreeOpenNodeScope(const SimpleNodePtr& /*n*/)
{
}

void PythonGrammar::jjtreeCloseNodeScope(const SimpleNodePtr& n)
{
    SimpleNodePtr peeked = getJJTree()->peekNode();
    QList<ObjectPtr> *specialTokens = getTokenSourceSpecialTokensList();
    bool after = true;
    if (n) {
        if (specialTokens->length() > 0) {
            if (!prev) {
                // it was not previously set, let's get the current and add it before that token
                after = false;
                prev = peeked;
            }

            foreach(ObjectPtr next, *specialTokens) {
                int strategy = STRATEGY_ADD_AFTER_PREV; // default strategy
                ObjectAndStrategyPtr os = next.dynamicCast<ObjectAndStrategy>();
                if (os) {
                    strategy = os->strategy;
                    next = os->obj;
                }

                if (strategy == STRATEGY_BEFORE_NEXT) { // try to set 'before the next' and not after prev token
                    addToPeek(peeked, next, false, Ast::Object);
                } else {
                    // may still add before the next, if there was no prev (we can check that by the 'after' variable)
                    // in this case, we'll do some checks to see if it is really correct (checking for the line and column)

                    if (next.dynamicCast<Token>()) {
                        addSpecial(findTokenToAdd(next.dynamicCast<Token>()), next, after);
                    } else {
                        addSpecial(prev, next, after);
                    }
                }
            }
            specialTokens->clear();
        }
        prev = peeked;
    }
}


/*public final void addSpecialToken(Object o) throws ParseException {
    if (!(o instanceof SpecialStr)) {
        o = convertStringToSpecialStr(o);
    }
    //the default is adding after the previous token
    getTokenSourceSpecialTokensList().add(new Object[] { o, STRATEGY_ADD_AFTER_PREV });
}


public final SpecialStr createSpecialStr(String token) throws ParseException {
    return createSpecialStr(token, DEFAULT_SEARCH_ON_LAST);
}*/


SpecialStrPtr PythonGrammar::createSpecialStr(const QString& token, bool searchOnLast, bool throwException)
{
    TokenPtr currentToken(getCurrentToken());

    TokenPtr firstTokenToIterate = searchOnLast ? getJJLastPos() : currentToken;
    TokenPtr foundToken(NULL);

    int foundAtPos = 0;

    //lot's of tokens, but we'll bail out on an indent, or dedent, so, that's OK.
    TokensIterator* iterTokens = getTokensIterator(firstTokenToIterate, 50, true);
    while (iterTokens->hasNext()) {
        foundAtPos += 1;
        TokenPtr next = iterTokens->next();
        if(next->image == token){
            //Found what we were looking for!
            foundToken = next;
            break;
        }
    }


    if (foundToken) {
        if(foundAtPos <= 2 //found at correct position.
            || searchOnLast //we already matched it... right now we're just adding it to the stack!
            ){
            return foundToken->asSpecialStr();
        }
    }
    if(throwException){
        //ParseException e = createException(token, currentToken);

        //we found it at the wrong position!
        if(foundToken != NULL){
            //we found it, but not on the position we were expecting, so, we must skip some tokens to get to it --
            //and report the needed exception)
            //addAndReport(e, "Found at wrong position: "+foundToken);
            TokenPtr beforeLastReturned = iterTokens->getBeforeLastReturned();
            setCurrentToken(beforeLastReturned);
            return foundToken->asSpecialStr();
        }

        //create a 'synthetic token' in the place we were expecting it.
        if(currentToken != NULL){
            CharStream* inputStream = getTokenManager()->getInputStream();

            int created = getTokenManager()->addCustom(currentToken, token);
            if(created != TokenManager::CUSTOM_NOT_CREATED){
                if(created == TokenManager::CUSTOM_CREATED_WAS_PARENS){
                    //if we had a parens, let's clear the tokens we iterated because we can have skipped indentations!
                    currentToken->next->next.clear();

                    //EOF was already found... let's restore the previous indentation level!
                    if(getTokenManager()->levelBeforeEof != -1){
                        getTokenManager()->level = getTokenManager()->levelBeforeEof;
                        getTokenManager()->levelBeforeEof = -1; //mark it as not found again.
                    }
                    inputStream->restoreLineColPos(currentToken->endLine, currentToken->endColumn);
                }
                //addAndReport(e, "Created custom token: "+token);
                return SpecialStrPtr(new SpecialStr(token, currentToken->beginLine, currentToken->beginColumn, currentToken->endLine, currentToken->endColumn, currentToken->startPos, currentToken->endPos));
            }
        }
        //throw e;
    }
    return SpecialStrPtr(NULL);
}

void PythonGrammar::makeString(const QString & s, int quotes, const SimpleNodePtr& strToFill)
{
    StrPtr outStr = strToFill.dynamicCast<Str>();
    //qDebug() << s;
    QChar quoteChar = s.at(0);
    int start = 0;
    bool ustring = true;
    bool bstring = false;
    if (quoteChar == 'u' || quoteChar == 'U') {
        ustring = true;
        start++;
    } else if (quoteChar == 'b' || quoteChar == 'B') {
        bstring = true;
        start++;
    }
    quoteChar = s.at(start);
    if (quoteChar == 'r' || quoteChar == 'R') {
        QString str = s.mid(quotes + start + 1, s.length() - quotes*2 - 1);
        outStr->setType_(getType(s.at(start + 1), quotes));
        outStr->setS(str);
        outStr->setUnicode(ustring);
        outStr->setRaw(true);
        outStr->setBinary(bstring);

    } else {
        int n = s.length() - quotes*2 - start;
        int i = quotes + start;

        QString str = s.mid(i, n);
        outStr->setType_(getType(s.at(start), quotes));
        outStr->setS(str);
        outStr->setUnicode(ustring);
        outStr->setRaw(false);
        outStr->setBinary(bstring);
    }
}

int PythonGrammar::getType(QChar c, int quotes)
{
    if (c == '\'')
        return quotes == 1 ? StrTypeType::SingleSingle : StrTypeType::TripleSingle;
    if (c == '"')
        return quotes == 1 ? StrTypeType::SingleDouble : StrTypeType::TripleDouble;
    return 0;
}


void PythonGrammar::makeInt(const QString& s, int radix, const TokenPtr& token, const SimpleNodePtr& numberToFill)
{
    NumPtr num = numberToFill.dynamicCast<Num>();

    num->setNum(token->image);

    if (s.endsWith("L") || s.endsWith("l")) {
        num->setType_(NumTypeType::Long);
        return;
    }

    int ndigits = s.length();
    int i = 0;
    while (i < ndigits && s.at(i) == '0')
        i++;

    if ((ndigits - i) > 11) {
        num->setType_(NumTypeType::Long);
        return;
    }

    bool ok = true;
    long l = s.toLong(&ok, radix);
    if (!ok){
        //handleNumberFormatException(token);
        l = 0;
    }
    if (l > 0xffffffffl || (radix == 10 && l > INT_MAX)) {
        num->setType_(NumTypeType::Long);
        return;
    }
    num->setType_(NumTypeType::Int);
}

void PythonGrammar::makeFloat(const TokenPtr& t, const SimpleNodePtr& numberToFill)
{
    NumPtr num = numberToFill.dynamicCast<Num>();
    num->setNum(t->image);
    num->setType_(NumTypeType::Float);
}

void PythonGrammar::makeComplex(const TokenPtr& t, const SimpleNodePtr& numberToFill)
{
     /*String s = t.image;
     String compNumber = s.substring(0, s.length() - 1);
     numberToFill.num = s;
     try {
                     numberToFill.n = Double.valueOf(compNumber);
             } catch (NumberFormatException e) {
                     handleNumberFormatException(t,  e);
             }
     numberToFill.type = Num.Comp;*/
}

SpecialStrPtr PythonGrammar::convertStringToSpecialStr(const ObjectPtr& o)
{
    if (o.dynamicCast<SpecialStr>()) {
        return o.dynamicCast<SpecialStr>();
    } else {
        if(o.dynamicCast<Token>()){
            return o.dynamicCast<Token>()->asSpecialStr();
        }
        qDebug() << "WHAT";
        return SpecialStrPtr();//createSpecialStr(o, DEFAULT_SEARCH_ON_LAST, false);//o.dynamicCast<OString>()->trim()
    }
}

TokensIterator* PythonGrammar::getTokensIterator(const TokenPtr& firstIterationToken, int tokensToIterate, bool breakOnIndentsDedentsAndNewCompounds)
{
    if (_tokensIterator == NULL) {
        _tokensIterator = new TokensIterator(getTokenManager(), firstIterationToken, tokensToIterate, breakOnIndentsDedentsAndNewCompounds);
    } else {
        _tokensIterator->reset(getTokenManager(), firstIterationToken, tokensToIterate, breakOnIndentsDedentsAndNewCompounds);
    }
    return _tokensIterator;
}

